package org.showminifier.web;

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.logging.Logger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.GZIPOutputStream;

import javax.jdo.JDOObjectNotFoundException;
import javax.jdo.PersistenceManager;
import javax.servlet.http.HttpServletRequest;

import org.showminifier.core.Options;

import com.google.appengine.api.datastore.Key;
import com.google.appengine.api.datastore.KeyFactory;
import com.google.appengine.api.users.User;

/**
*
* Show Minifier
* Author: Shihua Ma <beijing.josh@gmail.com>
* Copyright (c) 2010 Shihua Ma
* Licensed under the MIT license.
*
*/

public class Util {
	private static final Logger log = Logger.getLogger(Util.class.getName());
	
	public static final HashMap<String, Boolean> options = new HashMap<String, Boolean>();
	static {
		options.put("collapse-boolean-attributes",true);
		options.put("collapse-whitespace",true);
		options.put("remove-attribute-quotes",true);
		options.put("remove-comments-from-cdata",true);
		options.put("remove-comments",true);
		options.put("remove-cdata-sections-from-cdata",true);
		options.put("remove-empty-attributes",true);
		options.put("remove-empty-elements",false);
		options.put("remove-optional-tags",true);
		options.put("remove-redundant-attributes",true);
		options.put("remove-url-scheme",true);
		options.put("use-short-doctype",true);
		options.put("remove-script-type-attributes",false);
		options.put("remove-style-type-attributes",false);
	}
	
	public static Key getKeyForSetting(User user){
	    return KeyFactory.createKey(Settings.class.getSimpleName(),
                user.getNickname());
	}

	public static Settings getUserSetting(User user){
		PersistenceManager pm = PMF.get().getPersistenceManager();
	    Settings settings = null;
	    try{
	    	settings = pm.getObjectById(Settings.class,getKeyForSetting(user));
	    }catch (JDOObjectNotFoundException e) {
	    	log.info("Method getUserSetting could not find user(" + user.getNickname() + ") settings");
	    }
		return settings;
	}


	public static String prettyName(String inp,boolean onlyLetter) {
		StringBuilder sb = new StringBuilder();
		int a = 'a', z = 'z', A = 'A', Z = 'Z';
		boolean to = true;
		for(int i = 0,len = inp.length(); i < len; i++){
			int ch = inp.charAt(i);
			if(to){
				if(ch >= a || ch <=z){
					ch = ch - 32;
					to = false;
				}else if(ch >= A || ch <=Z){
					to = false;
				}
			}
			if((ch < a || ch > z) && (ch < A || ch > Z))
				to = true;
			if(onlyLetter){
				sb.append(Character.isLetter((char)ch) ? Character.toString((char)ch) : "");
			}else{
				sb.append(Character.toString((char)ch));
			}
		}
		return sb.toString();
	}
	
	
	public static boolean isDefaultOptions(HttpServletRequest req){
		for(String key :options.keySet()){
			String name = req.getParameter(key);
			boolean value = name == null ? false : name.equalsIgnoreCase("on");
			if(value != options.get(name)){
				return false;
			}
		}		
		return true;
	}
	
	public static Options defuultOptions(){
		return buildOptions(null, false);
	}
	
	public static Options buildOptions(HttpServletRequest req, boolean override){
		Options option = new Options();
		for(String key : options.keySet()){
			Method init = null;
			Object[] object =  req == null ?
								new Object[]{options.get(key)} : override ?
								new Object[]{true} : new Object[]{defaultOption(req.getParameter(key), true)};
		    try {
		      init = Options.class.getDeclaredMethod("set"+prettyName(key,true),new Class[]{boolean.class});
		      init.invoke(option,object);
		    } catch (Exception ex) {
		    	log.severe("Cound not get method[" + "set"+prettyName(key,true) +"] at Options.class");
		    	throw new RuntimeException(ex);
		    }
		}
		return option;		
	}
	
	public static boolean defaultOption(String param,boolean defaultValue){		
		return param == null ? defaultValue : param.equalsIgnoreCase("on") ? true : false;
	}
	
	public static int defaltedLength(String content, String charset) throws IOException{
		ByteArrayOutputStream ba = new ByteArrayOutputStream();
		defalteOutput(content,charset, ba);
		return ba.size();
	}
	
	public static void defalteOutput(String content, String charset, OutputStream out) throws IOException{
		GZIPOutputStream gzip = new GZIPOutputStream(out);
        OutputStreamWriter osw = new OutputStreamWriter(gzip, charset);
        osw.write(content);
        osw.flush();
        osw.close();
	}
	
	public static String repalceBaseTag(String html, String baseLink, String script){
		String modifyString ="\r\n<!------------------Show Minifier modified base attribute for display purpose------------------>\r\n";
		Pattern p = Pattern.compile("(?:<(base)((?:\\s[\\w:-]+\\s*=\\s*(?:(?:\"[^\"]*\")|(?:'[^']*')||[^>\\s]+))*)(?:\\/?)>\\s*(?:</\\1>)?)");
        Matcher m = p.matcher(html);
        if(m.find()){
        	int start = m.group(2).indexOf("href=");
    	   if(start > -1){
    		   return m.replaceFirst(m.group() + (script == null ? "" : script));
    	   }else{
    		   return m.replaceFirst(modifyString + "<" + m.group(1) + " href=\"" + baseLink + "\"" + m.group(2) + ">\r\n" + (script == null ? "" : script));
    	   }
       }else{
    	   String headTag = "(<head[^>]*>)";
    	   p = Pattern.compile(headTag);
           m = p.matcher(html);
           if(m.find()){
        	   return (m.replaceFirst(m.group() + (baseLink == null ? "" : modifyString + "<base href=\"" + baseLink + "\">\r\n") + (script == null ? "" : script)));       	   
           }else{
        	   String htmlTag = "(<html[^>]*>)";
        	   p = Pattern.compile(htmlTag);
               m = p.matcher(html);
               if(m.find())
            	   return (m.replaceFirst(m.group() + (baseLink == null ? "" : modifyString + "<base href=\"" + baseLink + "\">\r\n") + (script == null ? "" : script)));      	   
           }
       }
       return (baseLink == null ? "" : modifyString + "<base href=\"" + baseLink + "\">\r\n") + (script == null ? "" : script) + html;
	}
	
	public static String getBaseHref(String link){
		Pattern urlRegex = Pattern.compile("^(?:(?:http[s]?)://[-\\w]+(?:\\.\\w[-\\w]*)+)(?::\\d+)?$");
		Matcher m = urlRegex.matcher(link);
	    if(m.find()){
	    	return link + "/";
	    }
		int start = link.lastIndexOf('/');
		if(start > -1){
			return link.substring(0, start + 1);
		}
		return null;
	}
	
	private static String strUrl = 
			"(" +
				"(?:http[s]?://)(?:([-\\w]+(?:\\.\\w[-\\w]*)+)" +
				"|" +
				"(?:(?i:[a-z0-9]|[a-z0-9][a-z0-9]*[a-z0-9])" +
				"(?x-i:com\\b|edu\\b|biz\\b|in(?:t|fo)\\b|mil\\b|net\\b|org\\b|[a-z][a-z]\\b))" + 
			")" +
			"(?::\\d+)?" +
			"(?:/?)" + 
			"(?:[^;\"'<>()\\[\\]{}\\s\\x7F-\\xFF!.,?]*)" +
			"(?:[!.,?]+[^;\"'<>()\\[\\]{}\\s\\x7F-\\xFF!.,?]+)*" +
			")" ;
	private static Pattern ipRegex = 
		Pattern.compile("^((?:(?:[01]?\\d\\d?|2[0-4]\\d|25[0-5])\\.){3}(?:[01]?\\d\\d?|2[0-4]\\d|25[0-5]))$");
	
	private static Pattern urlRegex = Pattern.compile(strUrl);
	
	public static boolean isValidURL(String url){
		url = url.trim();
	    Matcher m = urlRegex.matcher(url);
	    return m.matches();
	}
	
	
	
    public static boolean isIpAddress(String s){
        Matcher m = ipRegex.matcher(s);
        return m.matches();
    }


    public static String getClientAddress(HttpServletRequest req) {
        String address = req.getHeader("X-Forwarded-For");
        if (address != null && isIpAddress(address)) {
            return address;
        }
        return req.getRemoteAddr();
    }
}
